package me.illtamer.serializer;

import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import lombok.extern.slf4j.Slf4j;
import me.illtamer.rpc.entity.RpcRequest;
import me.illtamer.rpc.entity.RpcResponse;
import me.illtamer.rpc.enumeration.SerializerCode;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;

/**
 * 涉及到的数据类中必须拥有无参构造函数
 * */
@Slf4j
public class KryoSerializer implements CommonSerializer {

    /**
     * 使用 ThreadLocal 初始化 Kryo(其 #output 与 #input 线程不安全)
     * */
    private static final ThreadLocal<Kryo> kryoThreadLocal = ThreadLocal.withInitial(() -> {
        Kryo kryo = new Kryo();
        // 注册类与指定 id 关联，简化数据量
        kryo.register(RpcResponse.class);
        kryo.register(RpcRequest.class);
        // 循环引用检测
        kryo.setReferences(true);
        // 不强制要求注册类，默认为 false，若设置为 true 则要求涉及到的所有类都要注册，包括 Object
        kryo.setRegistrationRequired(false);
        return kryo;
    });

    @Override
    public byte[] serialize(Object object) {
        try (Output output = new Output(new ByteArrayOutputStream())) {
            Kryo kryo = kryoThreadLocal.get();
            kryo.writeObject(output, object);
            kryoThreadLocal.remove();
            return output.toBytes();
        }
    }

    @Override
    public Object deserialize(byte[] bytes, Class<?> clazz) {
        try (Input input = new Input(new ByteArrayInputStream(bytes))) {
            Kryo kryo = kryoThreadLocal.get();
            Object object = kryo.readObject(input, clazz);
            kryoThreadLocal.remove();
            return object;
        }
    }

    @Override
    public int getCode() {
        return SerializerCode.valueOf("KRYO").getCode();
    }

}
